import { app, BrowserWindow, Notification, shell } from 'electron'
import path from 'path';
import fs from 'fs-extra';
import Searcher from './searcher';
import { IItem, ItemType, SearchItem } from '../type';
import { exec } from 'child_process';

/**运行项 */
class Item implements IItem {
    /**展示名 */
    public name: string = '';
    public type: ItemType = ItemType.None;
    public param: string = '';

    /**用于搜索的 Key */
    public searchedKey: string = '';

    constructor(name: string, typeStr: string, param: string, searchedKey: string = '') {
        this.name = name;
        this.param = param;
        this.searchedKey = searchedKey;

        switch (typeStr) {
            case 'p': {
                this.type = ItemType.Program;
                break;
            }
            case 'w': {
                this.type = ItemType.Web;
                break;
            }
            case 'f': {
                this.type = ItemType.File;
                break;
            }
            case 'd': {
                this.type = ItemType.Dir;
                break;
            }
            case 'c': {
                this.type = ItemType.CMD;
                break;
            }
            case 'b': {
                this.type = ItemType.BuiltIn;
                break;
            }
        }
    }
}

export class QuickStart {
    private itemList: Item[] = [];
    private searcher: Searcher = new Searcher();
    private searchResult: Item[] = [];
    private selectionIndex: number = 0;
    public onReload: (() => void) | null = null;

    public load() {
        let config: string = '';
        try {
            config = fs.readFileSync(this.getConfigPath(), 'utf-8');
        } catch (e: any) {
            new Notification({ title: '配置文件不存在', body: e.toString() }).show();
        }

        this.itemList = this.parseConfig(config);
        this.addBuiltInItem(this.itemList);

        let searchItems: SearchItem[] = [];
        for (let i = 0; i < this.itemList.length; i++) {
            searchItems.push(new SearchItem(i, this.itemList[i].searchedKey));
        }

        this.searcher.load(searchItems);
    }

    /**
     * 搜索
     * @param query 搜索内容
     * @returns Item[]
     */
    public search(query: string): IItem[] {
        this.searchResult = [];
        let ids = this.searcher.search(query);
        for (let id of ids) this.searchResult.push(this.itemList[id]);
        return this.searchResult;
    }

    public resetSearch() {
        this.searchResult = [];
        this.resetSelection();
    }

    public resetSelection() { this.selectionIndex = 0; }
    public getSelectionIndex(): number { return this.selectionIndex; }
    public downMoveSelection() {
        this.selectionIndex++;
        if (this.selectionIndex >= this.searchResult.length) this.selectionIndex = this.searchResult.length - 1;
    }
    public upMoveSelection() {
        this.selectionIndex--;
        if (this.selectionIndex < 0) this.selectionIndex = 0;
    }

    public execSelection() {
        if (this.selectionIndex < 0 || this.selectionIndex >= this.searchResult.length) return;
        let item = this.searchResult[this.selectionIndex];
        switch (item.type) {
            case ItemType.None: {
                new Notification({ title: '执行失败', body: '未知类型项' }).show();
                break;
            }
            case ItemType.Program: this.execProgram(item); break;
            case ItemType.Web: this.execWeb(item); break;
            case ItemType.File: this.execFile(item); break;
            case ItemType.Dir: this.execDir(item); break;
            case ItemType.CMD: this.execCMD(item); break;
            case ItemType.BuiltIn: this.execBuiltIn(item); break;
        }
    }

    private parseConfig(config: string): Item[] {
        if (config == '') return [];

        let items: Item[] = [];
        let rows: string[] = config.split(/\r?\n/).map(line => line.trim()).filter(line => line.length > 0);
        for (let row of rows) {
            let item = this.parseRow(row);
            if (item == null) {
                new Notification({ title: '配置项解析失败', body: `${row}\n应用自动退出...` }).show();
                return [];
            }
            items.push(item);
        }
        return items;
    }

    private parseRow(rowStr: string): Item | null {
        const reg = /(.+)\|(.)\((.+)\)/;
        let result = rowStr.match(reg);
        if (result == null) return null;
        if (result?.length != 4) return null;

        let item = new Item(result[1], result[2], result[3]);

        // 设置 Key
        let arr = item.name.split('&');
        if (arr.length == 1) item.searchedKey = item.name;
        else {
            item.name = arr[0];
            item.searchedKey = arr[1];
        }
        if (item.type == ItemType.None) return null;
        return item;
    }

    /**获得配置文件路径 */
    private getConfigPath(): string {
        // 生产环境配置文件在应用根目录
        if (app.isPackaged) return path.join(path.dirname(process.execPath), 'config.txt');
        else return path.join(__dirname, '../../public/config.txt');
    }

    private addBuiltInItem(items: Item[]) {
        items.push(new Item('reload', 'b', '', 'reload'));
        items.push(new Item('quit', 'b', '', 'quit'));
        items.push(new Item('config', 'b', this.getConfigPath(), 'config'));
    }

    private execProgram(item: Item) {
        fs.pathExists(item.param, (err, exists: boolean) => {
            if (exists) {
                shell.openPath(item.param).catch(err => {
                    new Notification({ title: '打开应用失败', body: err }).show();
                });
            }
            else new Notification({ title: '打开应用失败', body: `路径不存在 ${item.param}` }).show();
        });
    }
    private execWeb(item: Item) {
        shell.openPath(item.param).catch(err => {
            new Notification({ title: '打开网站失败', body: err }).show();
        });
    }
    private execFile(item: Item) {
        fs.pathExists(item.param, (err, exists: boolean) => {
            if (exists) {
                shell.openPath(item.param).catch(err => {
                    new Notification({ title: '打开路径失败', body: err }).show();
                });
            }
            else new Notification({ title: '打开路径失败', body: `路径不存在 ${item.param}` }).show();
        });
    }
    private execDir(item: Item) {
        this.execFile(item);
    }
    private execCMD(item: Item) {
        exec(item.param, { windowsHide: true }, (err, stdout, stderr) => {
            if (err) new Notification({ title: '执行命令失败', body: stderr }).show();
        });
    }
    private execBuiltIn(item: Item) {
        switch (item.name) {
            case 'reload': {
                this.load();
                this.resetSearch();
                if (this.onReload) this.onReload();
                break;
            }
            case 'quit': app.quit(); break;
            case 'config': {
                this.execFile(item);
                break;
            }
        }
    }
}
